package com.avic.controller;

import com.avic.common.constant.AppConstant;
import com.avic.common.constant.SystemLogConstant;
import com.avic.common.constant.UserConstant;
import com.avic.common.util.AssertUtil;
import com.avic.common.util.FileUploadUtil;
import com.avic.common.util.ex.BizErrorCode;
import com.avic.common.util.ex.BizException;
import com.avic.common.util.lang.BaseResponse;
import com.avic.common.util.lang.ServiceCallBack;
import com.avic.controller.info.FileListInfo;
import com.avic.controller.request.GetFileListReq;
import com.avic.controller.request.UpdateLoadConfigReq;
import com.avic.controller.result.GetFileListRes;
import com.avic.controller.result.UploadFileRes;
import com.avic.dal.condition.PageQueryFileListCondition;
import com.avic.dal.model.FileConfigManager;
import com.avic.dal.model.FileManager;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;


@Slf4j
@RestController
@RequestMapping("file")
@Api(tags = "文件管理", description = "文件管理")
public class FileController extends AbstractFacadeImpl {

    @ApiOperation(value = "分页获取附件列表")
    @PostMapping("list")
    public GetFileListRes getInformationReportList(@RequestBody GetFileListReq requestBody) {
        final GetFileListRes result = new GetFileListRes();
        return serviceExecuteTemplate.executeService(result, new ServiceCallBack() {
            @Override
            public void check() throws Exception {
            }

            @Override
            public void doService() throws Exception {
                PageQueryFileListCondition pageQueryFileListCondition = new PageQueryFileListCondition(
                        requestBody.getCurrentPage(), requestBody.getPageSize()
                );
                pageQueryFileListCondition.setFileName(requestBody.getFileName());
                pageQueryFileListCondition.setUploadBeginTime(requestBody.getUploadBeginTime());
                pageQueryFileListCondition.setUploadEndTime(requestBody.getUploadEndTime());

                List<FileManager> fileManagers = fileService.itemList(pageQueryFileListCondition);

                List<FileListInfo> fileListInfos = new ArrayList<>();
                fileManagers.forEach(fileManager -> {
                    FileListInfo fileListInfo = new FileListInfo();
                    fileListInfo.setId(fileManager.getId());
                    fileListInfo.setFileName(fileManager.getFileName());
                    fileListInfo.setFileSize(fileManager.getFileSize());
                    fileListInfo.setUploadTime(StringUtils.isNotBlank(fileManager.getUploadTime()) ?
                            StringUtils.substringBefore(fileManager.getUploadTime(), AppConstant.MYBATIS_DATA_TIME_SEPARATOR) : StringUtils.EMPTY);
                    fileListInfo.setFileUrl(commonProperties.getViewFilePrefix() + fileManager.getFileUrl());
                    fileListInfo.setFileType(fileManager.getFileType());
                    fileListInfos.add(fileListInfo);
                });
                result.setFileListInfos(fileListInfos);
                result.setTotalRecords(fileService.queryRecordsTotal(pageQueryFileListCondition));
                result.setSuccess(true);
            }
        });
    }

    @ApiOperation(value = "上传附件")
    @PostMapping("/upload")
    public UploadFileRes uploadImage(MultipartFile multipartFile,
                                     @RequestHeader(value = UserConstant.HEADER_CURRENT_USER_ID_KEY, required = false) String currentUserId) {
        final UploadFileRes result = new UploadFileRes();
        FileConfigManager fileConfigManager = fileService.selectFileCountConfig();
        return serviceExecuteTemplate.executeService(result, new ServiceCallBack() {
            @Override
            public void check() throws Exception {
                AssertUtil.isTrue(null != multipartFile, BizErrorCode.PARAM_ERROR, "上传的文件不能为空");
                AssertUtil.isTrue(null != fileConfigManager, BizErrorCode.SYSTEM_ERROR, "上传文件配置参数为空");
                // MB为单位
                long fileSizeLimit = fileConfigManager.getFileSizeConfig() * 1024 * 1024;
                AssertUtil.isTrue(multipartFile.getSize() <= fileSizeLimit,
                        BizErrorCode.PARAM_ERROR, "文件超过【" + fileConfigManager.getFileSizeConfig() + "】M，不可上传");
                int fileTotalSize = fileService.fileTotalCount();
                int fileTotalCountLimit = fileConfigManager.getFileCountConfig();
                AssertUtil.isTrue(fileTotalSize <= fileTotalCountLimit,
                        BizErrorCode.PARAM_ERROR, "文件数量超过最大配置【" + fileTotalCountLimit + "】，不可上传");
            }

            @Override
            public void doService() throws Exception {
                FileManager fileManager = new FileManager();
                if (multipartFile.getSize() <= 1024 * 1024) {
                    fileManager.setFileSize(new BigDecimal(multipartFile.getSize()).divide(new BigDecimal(1024), 2, BigDecimal.ROUND_HALF_UP).toString() + "KB");
                } else if (multipartFile.getSize() > 1024 * 1024) {
                    fileManager.setFileSize(new BigDecimal(multipartFile.getSize()).divide(new BigDecimal(1048576), 2, BigDecimal.ROUND_HALF_UP).toString() + "MB");
                }
                BufferedInputStream bufferedInputStream = null;
                try {
                    // 获取文件名
                    String originalFilename = FileUploadUtil.getWindos(multipartFile.getOriginalFilename());
                    log.info("上传的文件名为：" + originalFilename);
                    fileManager.setFileName(originalFilename);

                    // 获取文件的后缀名
                    String suffixName = originalFilename.substring(originalFilename.lastIndexOf("."));
                    log.info("上传的后缀名为：" + suffixName);
                    fileManager.setFileType(suffixName);

                    // 文件上传后的路径
                    String filePath = commonProperties.getUploadLocation();
                    StringBuffer fileName = new StringBuffer(FileUploadUtil.FILE_MODULE).append(UUID.randomUUID().toString()).append("-").append(originalFilename);
                    File dest = new File(filePath + fileName);
                    // 检测是否存在目录
                    if (!dest.getParentFile().exists()) {
                        dest.getParentFile().mkdirs();
                    }
                    multipartFile.transferTo(dest);
                    fileManager.setFileUrl(fileName.toString());

                } catch (IOException e) {
                    log.error("上传文件时发生错误 {}", e.getMessage());
                    throw new BizException(BizErrorCode.SYSTEM_ERROR, "上传文件时发生错误");
                } finally {
                    try {
                        bufferedInputStream.close();
                    } catch (Exception e) {
                    }
                }
                result.setFileId(fileService.createFileRecord(fileManager));

                // 新建系统日志
                systemServiceFeignService.createSystemLog(
                        String.format(SystemLogConstant.UPLOAD_FILE, result.getFileId()), currentUserId);
                result.setSuccess(true);
            }
        });
    }

    @ApiOperation(value = "删除附件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "itemId", paramType = "path", value = "资源ID", required = true)
    })
    @DeleteMapping("/{itemId}")
    public BaseResponse deleteFile(@PathVariable("itemId") int itemId,
                                   @RequestHeader(value = UserConstant.HEADER_CURRENT_USER_ID_KEY, required = false) String currentUserId) {
        final BaseResponse result = new BaseResponse();
        return serviceExecuteTemplate.executeService(result, new ServiceCallBack() {
            @Override
            public void check() throws Exception {
                AssertUtil.isTrue(itemId > 0, BizErrorCode.PARAM_ERROR, "数据ID大于零");

            }

            @Override
            public void doService() throws Exception {
                fileService.delete(itemId);

                // 新建系统日志
                systemServiceFeignService.createSystemLog(
                        String.format(SystemLogConstant.DELETE_FILE, itemId), currentUserId);
                result.setSuccess(true);
            }
        });
    }

    @ApiOperation(value = "更新上传文件大小配置")
    @PutMapping("config")
    public BaseResponse deleteFile(@RequestBody UpdateLoadConfigReq updateLoadConfigReq) {
        final BaseResponse result = new BaseResponse();
        return serviceExecuteTemplate.executeService(result, new ServiceCallBack() {
            @Override
            public void check() throws Exception {
                AssertUtil.isTrue(updateLoadConfigReq.getFileCountLimit() > 0, BizErrorCode.PARAM_ERROR, "文件数量设置不能小于零");

            }

            @Override
            public void doService() throws Exception {
                fileService.updateFileCountConfig(updateLoadConfigReq.getFileCountLimit());

                result.setSuccess(true);
            }
        });
    }

    @ApiOperation(value = "下载文件")
    @GetMapping("/download")
    public BaseResponse downloadFile(HttpServletResponse response, @RequestParam("fileId") int fileId) {
        final BaseResponse result = new BaseResponse();
        return serviceExecuteTemplate.executeService(result, new ServiceCallBack() {
            @Override
            public void check() throws Exception {

            }

            @Override
            public void doService() throws Exception {

                FileManager fileManager = fileService.getItemById(fileId);
                AssertUtil.isTrue(null != fileManager, BizErrorCode.PARAM_ERROR, "数据不存在");

                log.info("下载文件 {}", fileManager.getFileUrl());

                // 获取指定目录下的第一个文件
                File file = new File(commonProperties.getUploadLocation() + fileManager.getFileUrl());
                // 如果文件名不为空，则进行下载
                if (file.exists()) {
                    //下载的文件名
                    String fileName = file.getName();

                    // 如果文件名存在，则进行下载
                    if (file.exists()) {
                        // 配置文件下载
                        response.setHeader("content-type", "application/octet-stream");
                        response.setContentType("application/octet-stream");
                        // 下载文件能正常显示中文
                        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));

                        // 实现文件下载
                        byte[] buffer = new byte[1024];
                        FileInputStream fis = null;
                        BufferedInputStream bis = null;
                        try {
                            fis = new FileInputStream(file);
                            bis = new BufferedInputStream(fis);
                            OutputStream os = response.getOutputStream();
                            int i = bis.read(buffer);
                            while (i != -1) {
                                os.write(buffer, 0, i);
                                i = bis.read(buffer);
                            }
                            log.info("Download the file successfully!");
                        } catch (Exception e) {
                            log.info("Download the file failed! {}", e.getMessage());
                        } finally {
                            if (bis != null) {
                                try {
                                    bis.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                            if (fis != null) {
                                try {
                                    fis.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
                result.setSuccess(true);
            }
        });
    }
}
